/***
 * Copyright (c) 2021-2031 murenchao
 * fig is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *       http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package cool.taomu.software.fig.objectpool

import cool.taomu.software.fig.classloader.FigClassLoaderManage
import cool.taomu.software.fig.reflect.ReflectUtils
import java.lang.annotation.Annotation
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import java.util.concurrent.atomic.AtomicInteger
import org.apache.commons.lang3.builder.ToStringBuilder
import org.apache.commons.pool2.BaseKeyedPooledObjectFactory
import org.apache.commons.pool2.PooledObject
import org.apache.commons.pool2.impl.DefaultPooledObject
import org.apache.commons.pool2.impl.GenericKeyedObjectPool
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig
import org.eclipse.xtend.lib.annotations.Accessors
import org.slf4j.LoggerFactory
import cool.taomu.software.fig.configure.ConfigureManage

@Accessors
class ObjectPool<T> implements AutoCloseable {
    int id
    T instance

    new(T instance, int id) {
        this.instance = instance
        this.id = id
    }

    override close() throws Exception {
        ObjectPoolFactory.returnObject(instance.class as Class<T>, this);
    }

    override toString() {
        return ToStringBuilder.reflectionToString(this)
    }
}

class ObjectPoolFactory<T> extends BaseKeyedPooledObjectFactory<Class<T>, ObjectPool<T>> {

    var idCount = new AtomicInteger(1);
    val static LOG = LoggerFactory.getLogger(ObjectPoolFactory)
    static Object pool = null;

    def static synchronized <T> GenericKeyedObjectPool<Class<T>, ObjectPool<T>> getInstance() {
        if (pool === null) {
            var config = new GenericKeyedObjectPoolConfig<ObjectPool<T>>();
            var yml = ConfigureManage.loadConfig;
            config.setMaxTotalPerKey(yml.pool.maxTotalPerKey);
            config.setMaxIdlePerKey(yml.pool.maxIdlePerKey);
            config.setMinIdlePerKey(yml.pool.minIdlePerKey);
            config.setLifo(yml.pool.lifo);
            pool = new GenericKeyedObjectPool<Class<T>, ObjectPool<T>>(new ObjectPoolFactory<T>(), config);
        }
        return pool as GenericKeyedObjectPool<Class<T>, ObjectPool<T>>
    }

    def static <T> perparePool(Class<T> key) {
        ObjectPoolFactory.instance.preparePool(key)
    }

    def static <T> borrowObject(Class<T> key) {
        ObjectPoolFactory.instance.borrowObject(key)
    }

    def static <T> returnObject(Class<T> key, ObjectPool<T> obj) {
        ObjectPoolFactory.instance.returnObject(key, obj)
    }

    override PooledObject<ObjectPool<T>> makeObject(Class<T> key) throws Exception {
        LOG.info("创建对象:" + key)
        return super.makeObject(key);
    }

    override activateObject(Class<T> key, PooledObject<ObjectPool<T>> p) throws Exception {
        LOG.info("活动对象：" + key.name + " ==> " + p.object.id)
        super.activateObject(key, p);
    }

    override destroyObject(Class<T> key, PooledObject<ObjectPool<T>> p) throws Exception {
        LOG.info("销毁对象:" + key + " ==> " + p.object.id)
        super.destroyObject(key, p);
    }

    override passivateObject(Class<T> key, PooledObject<ObjectPool<T>> p) throws Exception {
        LOG.info("归还对象:" + key + " ==> " + p.object.id)
        super.passivateObject(key, p);
    }

    var figLoader = new FigClassLoaderManage();

    override wrap(ObjectPool<T> value) {
        return new DefaultPooledObject<ObjectPool<T>>(value);
    }

    override create(Class<T> zlass) throws Exception {
        var instance = figLoader.getBean(zlass) as T;
        var fields = ReflectUtils.getReflectFields(zlass);
        for (field : fields) {
            var ClassLoader loader = Thread.currentThread().getContextClassLoader();
            try {
                var springAutowired = "org.springframework.beans.factory.annotation.Autowired";
                var ann = field.getAnnotation(loader.loadClass(springAutowired) as Class<Annotation>);
                if (ann !== null) {
                    ReflectUtils.setReflectFields(zlass, field.name, instance, this.getSpringBean(field.type))
                }
            }catch(ClassNotFoundException ex){
                
            }
        }
        return new ObjectPool<T>(instance, idCount.getAndAdd(1));
    }

    def Object getSpringBean(Class<?> zlass) {
        try {
            var ClassLoader loader = Thread.currentThread().getContextClassLoader();
            var Class<?> registerBeanToSpring = loader.loadClass(
                "cool.taomu.software.fig.springutils.RegisterBeanToSpring");
            if (registerBeanToSpring !== null) {
                var desc = MethodType.methodType(Object, Class);
                var methodHandle = MethodHandles.lookup().findStatic(registerBeanToSpring, "getBean", desc);
                var bean = methodHandle.invokeExact(zlass);
                bean !== null ? LOG.info("从spring中成功获取一个bean") : LOG.info("从spring中未获取到一个bean");
                return bean;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

}
